#include "ip.h"
#include "icmp.h"
#include "arpcache.h"
#include "rtable.h"
#include "arp.h"
#include "log.h"
#include <stdlib.h>
#include <assert.h>

// If the packet is ICMP echo request and the destination IP address is equal to the IP address of the iface, send ICMP echo reply.
// Otherwise, forward the packet.
// Tips:
// You can use struct iphdr *ip = packet_to_ip_hdr(packet); in ip.h to get the ip header in a packet.
// You can use struct icmphdr *icmp = (struct icmphdr *)IP_DATA(ip); in ip.h to get the icmp header in a packet.
void handle_ip_packet(iface_info_t *iface, char *packet, int len)
{
	// assert(0 && "TODO: function handle_ip_packet not implemented!");
	struct iphdr *ip = packet_to_ip_hdr(packet);
	struct icmphdr *icmp = (struct icmphdr *)IP_DATA(ip);
	// Check if the packet is ICMP echo request
	if (ip->protocol == IPPROTO_ICMP && icmp->type == ICMP_ECHOREQUEST) {
		// Check if the destination IP address is equal to the IP address of the iface
		if ((ntohl)(ip->daddr ) == iface->ip) {
			// Send ICMP echo reply
			log(DEBUG,"iface ip and dst ip is same, tranmisting icmp packet");
			icmp_send_packet(packet, len, ICMP_ECHOREPLY, 0);
			return;
		}
	}

	// If the packet is not ICMP echo request or the destination IP address is not equal to the IP address of the iface, forward the packet.
	log(DEBUG,"dst IP: " IP_FMT " is not equal to the IP address OR not ICMP echo request, forward it",NET_IP_FMT_STR(ip->daddr));
	ip_forward_packet(ntohl(ip->daddr), packet, len);
}

// When forwarding the packet, you should check the TTL, update the checksum and TTL.
// Then, determine the next hop to forward the packet, then send the packet by iface_send_packet_by_arp.
// The interface to forward the packet is specified by longest_prefix_match.
void ip_forward_packet(u32 ip_dst, char *packet, int len)
{
	// assert(0 && "TODO: function ip_forward_packet not implemented!");
	struct iphdr *ip = packet_to_ip_hdr(packet);
	// 传入的是host-byte order
	log(DEBUG,"forwarding the packet, dst ip is" IP_FMT,HOST_IP_FMT_STR(ip_dst));
	
	// Decrease TTL
	ip->ttl--;
	// Check if TTL is 0
	if (ip->ttl <= 0) {
		// Send ICMP time exceeded
		log(DEBUG,"ttl <0, sending icmp time exceeded packet");
		// todo

		icmp_send_packet(packet, len, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL);
		free(packet);
		return;
	}

	// Update checksum
	ip->checksum = ip_checksum(ip);
	// Find the next hop
	rt_entry_t *rt_entry = longest_prefix_match(ip_dst);

	if (rt_entry == NULL) {
		// Send ICMP destination unreachable
		log(WARNING,"fail to find rt_entry, sending icmp net unreach packet");
		icmp_send_packet(packet, len, ICMP_DEST_UNREACH, ICMP_NET_UNREACH);
		return;
	}
	// Send the packet by iface_send_packet_by_arp
	iface_info_t *iface = rt_entry->iface;
	if (iface == NULL) {
		// Send ICMP destination unreachable
		log(ERROR, "iface is NULL");
		icmp_send_packet(packet, len, ICMP_DEST_UNREACH, 0);
		return;
	}

	u32 next_hop = rt_entry->gw ? rt_entry->gw : ntohl(ip->daddr);
	iface_send_packet_by_arp(iface, next_hop, packet, len);
}